//
// Created by Immortal on 4/25/2021.
//

#include "Http.h"


Http::Http():m_strReqContent(m_strReqContent_t)
{
    //初始化
    Init();
}


Http::Http(const char* buf):m_strReqContent(m_strReqContent_t)
{
    //初始化
    Init();

    //读取
    if(Read(buf) == false)
    {
        m_pResCode = sc400;
    }

}

Http::~Http()
{
    //请求参数
    if(m_pliReqParam != NULL)
    {
        delete m_pliReqParam;
        m_pliReqParam = NULL;
    }
    //请求字段
    if(m_pmapReqField != NULL)
    {
        delete m_pmapReqField;
        m_pmapReqField = NULL;
    }

    //请求Cookie
    if(m_pmapReqCookie != NULL)
    {
        delete m_pmapReqCookie;
        m_pmapReqCookie = NULL;
    }

    //响应字段
    if(m_pliResField != NULL)
    {
        delete m_pliResField;
        m_pliResField = NULL;
    }

    //响应Cookie
    if(m_pliResCookie != NULL)
    {
        delete m_pliResCookie;
        m_pliResCookie = NULL;
    }

}

void Http::Init()
{
    m_err = ERROR_NO;
    m_rt = REQ_NULL;
    //请求参数 - GET
    m_pliReqParam = new std::list<std::pair<std::string, std::string>>;
    //请求字段
    m_pmapReqField = new std::map<std::string, std::string>;
    //请求Cookie
    m_pmapReqCookie = new std::map<std::string, std::string>;
    //请求内容长度
    m_nReqContentSize = 0;

    //响应状态码
    m_pResCode = sc200;
    //响应字段
    m_pliResField = new std::list<std::pair<std::string, std::string>>;
    //响应Cookie
    m_pliResCookie = new std::list<_RES_COOKIE>;
    //响应文件大小
    m_nResFileSize = -1;
    //响应内容是否为数据， true表示数据，false表示文件
    m_bIsData = true;
    //是否保持连接 - 默认不保持
    SetKeepAlive(false);
    //设置服务器字段
    SetResField("Server", "Racoon 1.0");
}


//获取错误
Http::ERROR Http::getError()
{
    return m_err;
}

//读取
bool Http::Read(const char* buf)
{
    if(buf == NULL)
    {
        return false;
    }

    //解析字段
    if(AnalysisField(buf, strlen(buf)) == false)
    {
        return false;
    }

    //提取Cookie
    auto itCookie = m_pmapReqField->find("Cookie");
    if(itCookie != m_pmapReqField->end())
    {
        //有Cookie 提取
        if(ExtractCookie(itCookie->second.c_str(), itCookie->second.size()) == false)
        {
            //提取失败 出错
            m_err = ERROR_COOKIE;
            return false;
        }
    }

    //提取内容长度
    auto itContentSize = m_pmapReqField->find("Content-Length");
    if(itContentSize != m_pmapReqField->end())
    {
        if(ExtractContentSize(itContentSize->second.c_str(), itContentSize->second.size()) == false)
        {
            //提取失败 出错
            return false;
        }
    }

    return true;
}

//获取文件大小
int Http::GetFileSize(const char* path)
{
    //打开文件
    FILE* wj = fopen(path, "rb");
    if(wj == NULL)
    {
        //打开失败，不存在，返回-1
        return -1;
    }

    //获取文件大小
    fseek(wj, 0, SEEK_END);
    int nFileSize = ftell(wj);
    fseek(wj, 0, SEEK_SET);

    //关闭文件
    fclose(wj);

    return nFileSize;
}

//获取响应头
void Http::GetResHead(std::string& strBuf, int nContentSize)
{
    strBuf = m_pResCode;
    strBuf += "\r\n";

    //响应字段
    for(auto it : *m_pliResField)
    {
        strBuf += it.first;
        strBuf += ": ";
        strBuf += it.second;
        strBuf += "\r\n";
    }

    //内容大小
    if(m_bIsData == true)
    {
        //数据
        //获取内容大小
        int nContentSize_ = m_strResContent.size();
        if(nContentSize_ > 0)
        {
            //大于0才添加
            strBuf += "Content-Length: ";
            strBuf += std::to_string(nContentSize_);
            strBuf += "\r\n";
        }
    }
    else
    {
        //大于0 才添加，否则不需要
        if(nContentSize > 0)
        {
            //文件
            //直接添加 因为是外部提供
            strBuf += "Content-Length: ";
            strBuf += std::to_string(nContentSize);
            strBuf += "\r\n";
        }
    }

    //响应Cookie
    for(auto it : *m_pliResCookie)
    {
        strBuf += "Set-Cookie: ";
        strBuf += it.strName;
        strBuf += "=";
        strBuf += it.strValue;

        if(it.strDomain.empty() == false)
        {
            strBuf += "; ";
            strBuf += "Domain";
            strBuf += "=";
            strBuf += it.strDomain;
        }

        if(it.strPath.empty() == false)
        {
            strBuf += "; ";
            strBuf += "path";
            strBuf += "=";
            strBuf += it.strDomain;
        }

        if(it.strMaxAge.empty() == false)
        {
            strBuf += "; ";
            strBuf += "Max-Age";
            strBuf += "=";
            strBuf += it.strDomain;
        }

        strBuf += "\r\n";

    }

    //结尾
    strBuf += "\r\n";
}

//解析字段
bool Http::AnalysisField(const char* buf, int n)
{
    if(buf == NULL || n <= 0)
    {
        m_err = ERROR_HEAD;
        return false;
    }

    //判断结尾
    if(buf[n - 4] != '\r' || buf[n - 3] != '\n' || buf[n - 2] != '\r' || buf[n - 1] != '\n')
    {
        m_err = ERROR_END;
        return false;
    }

    //根据\r\n解析每行
    std::vector<std::string> vLine;
    if(OpStr::SplitStr(buf, n, "\r\n", 2, &vLine) == false)
    {
        m_err = ERROR_HEAD;
        return false;
    }

    //是否为空
    if(vLine.empty())
    {
        m_err = ERROR_HEAD;
        return false;
    }

    //遍历每行
    for(auto it : vLine)
    {
        //根据 : 解析每列
        std::vector<std::string> vColumn;
        if (OpStr::SplitStr(it.c_str(), it.size(), ":", 1, &vColumn, true) == true)
        {
            m_pmapReqField->insert(std::make_pair(vColumn[0], vColumn[1]));
            continue;
        }

        //解析失败 可能是请求类型
        if(it.compare(0, 3, "GET") == 0)
        {
            m_rt = REQ_GET;
        }
        else if(it.compare(0, 4, "POST") == 0)
        {
            m_rt = REQ_POST;
        }
        else
        {
            //字段错误
            m_err = ERROR_FIELD;
            return false;
        }

        //是请求类型，解析请求类型
        std::vector<std::string> vReqType;
        if (OpStr::SplitStr(it.c_str(), it.size(), " ", 1, &vReqType) == false)
        {
            m_err = ERROR_TYPE;
            m_rt = REQ_NULL;
            return false;
        }

        if(vReqType.size() != 3)
        {
            //不等于3就是出错 因为 TYPE / HTTP/1.1
            m_err = ERROR_TYPE;
            m_rt = REQ_NULL;
            return false;
        }

        //提取请求 路径 后缀名
        std::string strReqPathParam = vReqType.at(1);
        ExtractPPS(strReqPathParam.c_str(), strReqPathParam.size());

    }

    return true;
}

//提取请求(path) 路径(param) 后缀(suffix)
bool Http::ExtractPPS(const char* buf, int n)
{
    std::string strPPS;
    strPPS.append(buf, n);

    //获取 问号
    int nIndex = OpStr::FindStr(buf, n, "?", 1);
    if(nIndex == -1)
    {
        //没有问号，直接就是路径
        m_strReqPath = buf;
        //提取后缀
        ExtractSuffix();
        return true;
    }

    //有问号
    //提取问号前边 - 路径
    m_strReqPath = strPPS.substr(0, nIndex);

    //提取后缀
    ExtractSuffix();

    //提取问号后边 - 参数
    nIndex++;
    std::string strReqParam = strPPS.substr(nIndex);

    //切割 &
    std::vector<std::string> vParam;
    if(OpStr::SplitStr(strReqParam.c_str(), strReqParam.size(), "&", 1, &vParam) == false)
    {
        //只有一个参数 切割 =
        std::vector<std::string> vParamContent;
        if(OpStr::SplitStr(strReqParam.c_str(), strReqParam.size(), "=", 1, &vParamContent, true) == false)
        {
            //错误
            return false;
        }

        //不等于2表示有错
        if(vParamContent.size() != 2)
        {
            //错误
            return false;
        }

        //添
        m_pliReqParam->push_back(std::make_pair(vParamContent.at(0), vParamContent.at(1)));

    }
    else
    {
        //遍历每一个
        for(auto it : vParam)
        {
            //只有一个参数 切割 =
            std::vector<std::string> vParamContent;
            if(OpStr::SplitStr(it.c_str(), it.size(), "=", 1, &vParamContent, true) == false)
            {
                //错误
                return false;
            }

            //不等于2表示有错
            if(vParamContent.size() != 2)
            {
                //错误
                return false;
            }

            //添
            m_pliReqParam->push_back(std::make_pair(vParamContent.at(0), vParamContent.at(1)));
        }
    }

    return true;
}

//提取后缀(suffix)
bool Http::ExtractSuffix()
{
    //提取后缀
    int nIndexSuffix = m_strReqPath.rfind(".");
    if(nIndexSuffix != -1)
    {
        nIndexSuffix++;
        //请求后缀
        m_strReqSuffix = m_strReqPath.substr(nIndexSuffix);
        //转换大写
        std::transform(m_strReqSuffix.begin(), m_strReqSuffix.end(), m_strReqSuffix.begin(), ::toupper);
    }
}


//提取Cookie
bool Http::ExtractCookie(const char* buf, int n)
{
    if(buf == NULL || n <= 0)
    {
        return false;
    }

    //切割每个Cookie
    std::vector<std::string> vColumn;
    if(OpStr::SplitStr(buf, n, ";", 1, &vColumn) == false)
    {
        return false;
    }

    //遍历每行
    for(auto it : vColumn)
    {
        //提取键值
        std::vector<std::string> vKeyValue;
        if (OpStr::SplitStr(it.c_str(), it.size(), "=", 1, &vKeyValue, true) == false)
        {
            return false;
        }

        if(vKeyValue.size() != 2)
        {
            //不等于2就是出错 因为 Key=Val
            return false;
        }

        //存储
        m_pmapReqCookie->insert(std::make_pair(vKeyValue[0], vKeyValue[1]));
    }


    return true;
}

//提取内容长度
bool Http::ExtractContentSize(const char* buf, int n)
{
    try
    {
        m_nReqContentSize = std::stoi(buf);
    }
    catch(...)
    {
        m_nReqContentSize = 0;
        return false;
    }

    return true;
}

//获取请求类型
Http::REQ_TYPE Http::GetReqType()
{
    return m_rt;
}

//获取请求类型，返回值为字串
const char* Http::GetReqTypeStr()
{
    switch (m_rt)
    {
        case REQ_NULL:
            return "";
            break;
        case REQ_GET:
            return "GET";
            break;
        case REQ_POST:
            return "POST";
            break;
    }

    return NULL;
}

//获取请求路径
const char* Http::GetReqPath()
{
    return m_strReqPath.c_str();
}

//获取请求后缀
const char* Http::GetReqSuffix()
{
    return m_strReqSuffix.c_str();
}

//获取请求字段
const char* Http::GetReqField(const char* pszKey)
{
    auto itVal = m_pmapReqField->find(pszKey);
    if(itVal == m_pmapReqField->end())
    {
        return NULL;
    }

    return itVal->second.c_str();
}


//获取请求Cookie
const char* Http::GetReqCookie(const char* pszName)
{
    auto itVal = m_pmapReqCookie->find(pszName);
    if(itVal == m_pmapReqCookie->end())
    {
        return NULL;
    }

    return itVal->second.c_str();
}

//获取请求内容长度
int Http::GetReqContentSize()
{
    return m_nReqContentSize;
}




//设置状态码
void Http::SetResStatusCode(const char* buf)
{
    m_pResCode = buf;
}

//获取响应状态码
const char* Http::GetResStatusCode()
{
    return m_pResCode;
}

//设置字段
void Http::SetResField(const char* key, const char* value)
{
    //遍历响应字段
    for(auto it : *m_pliResField)
    {
        //判断
        if(it.first.compare(key) == 0)
        {
            //删除
            m_pliResField->remove(it);
            //插入
            m_pliResField->push_back(std::make_pair(key, value));
            return;
        }
    }

    //不存在，插入
    m_pliResField->push_back(std::make_pair(key, value));
}

//设置内容类型
void Http::SetContentType(const char* buf)
{
    SetResField("Content-Type", buf);
}

//设置Cookie
void Http::SetCookie(const char* name, const char* value, const char* domain, const char* path, const char* max_age)
{
    //先判断是否已存在
    for(auto it : *m_pliResCookie)
    {
        if(it.strName.compare(name) == 0)
        {
            //存在则覆盖
            it.strName = name;
            it.strValue = value;

            //不为空则添加
            if(domain != NULL){it.strDomain = domain;}
            if(path != NULL){it.strPath = path;}
            if(max_age != NULL){it.strMaxAge = max_age;}

            return;
        }
    }

    //不存在构造新的
    RES_COOKIE res_cookie;
    res_cookie.strName = name;
    res_cookie.strValue = value;

    //不为空则添加
    if(domain != NULL){res_cookie.strDomain = domain;}
    if(path != NULL){res_cookie.strPath = path;}
    if(max_age != NULL){res_cookie.strMaxAge = max_age;}

    m_pliResCookie->push_back(res_cookie);
}


//设置内容
void Http::SetContent(const void* buf, int n)
{
    m_strResContent.clear();
    m_strResContent.append(static_cast<const char*>(buf), n);
    //响应内容为数据
    m_bIsData = true;
}

//追加内容
void Http::AppendContent(const void* buf, int n)
{
    m_strResContent.append(static_cast<const char*>(buf), n);
    //响应内容为数据
    m_bIsData = true;
}

//设置响应文件
bool Http::SetResFile(const char* path)
{
    //存储文件数据
    m_strResContent = path;
    //响应文件大小
    m_nResFileSize = GetFileSize(path);
    //响应内容为文件
    m_bIsData = false;

    return true;
}

//获取响应内容
const char* Http::GetResContent()
{
    return m_strResContent.c_str();
}

//获取响应文件大小
int Http::GetResFileSize()
{
    return m_nResFileSize;
}

//获取响应类型
bool Http::GetResType()
{
    return m_bIsData;
}

//设置是否保持连接
void Http::SetKeepAlive(bool b)
{
    m_bKeepAlive = b;
    if(m_bKeepAlive == true)
    {
        SetResField("Connection", "keep-alive");
    }
    else
    {
        SetResField("Connection", "close");
    }
}

//获取是否保持连接
bool Http::GetKeepAlive()
{
    return m_bKeepAlive;
}
